﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using GPSCrowdSource.Algorithms;

namespace GPSCrowdSource.DataStructures {
  
  /// <summary>
  /// Represents a 3 vector.
  /// </summary>
  public class Vector3 {

    /// <summary>
    /// Copy constructor
    /// </summary>
    public Vector3(Vector3 inCopy) {
      X = inCopy.X;
      Y = inCopy.Y;
      Z = inCopy.Z;
      }

    /// <summary>
    /// Create a 3 vector from the given array containing
    /// x,y,z values respectively.
    /// </summary>
    public Vector3(float[] inV) {
      X = inV[0];
      Y = inV[1];
      Z = inV[2];
      }

    /// <summary>
    /// Create a 3 vector from a x,y,z
    /// </summary>
    public Vector3(float x, float y, float z) {
      X = x;
      Y = y;
      Z = z;
      }

    /// <summary>
    /// Get the x coordinate.
    /// </summary>
    public float X { get; set; }
    
    /// <summary>
    /// Get the Y coordinate.
    /// </summary>
    public float Y { get; set; }

    /// <summary>
    /// Get the z coordinate.
    /// </summary>
    public float Z { get; set; }

    /// <summary>
    /// Get the angle (in radians) from 0 to PI between this vector and the given.
    /// </summary>
    public float GetAngle(Vector3 v) {
      float m1 = Magnitude;
      float m2 = v.Magnitude;
      float dot = this*v;
      return (float)Math.Acos(dot/m1*m2);
      }

    /// <summary>
    /// Get the magnitude of this vector.
    /// </summary>
    public float Magnitude {
      get {
        return (float)Math.Sqrt(Math.Pow(X,2) + Math.Pow(Y,2) + Math.Pow(Z,2));
        }
      } 

    /// <summary>
    /// Get the unit vector 
    /// </summary>
    public Vector3 Unit {
      get {
        float m = Magnitude;
        if(m == 0) return new Vector3(0,0,0);
        return new Vector3(X/m,Y/m,Z/m);
        }
      }

    /// <summary>
    /// Scalar product
    /// </summary>
    public static float operator*(Vector3 a, Vector3 b) {
      return a.X*b.X + a.Y*b.Y + a.Z*b.Z;
      }

    /// <summary>
    /// Scalar multiplication.
    /// </summary>
    public static Vector3 operator*(Vector3 a, double v) {
      return new Vector3((float)(a.X*v),(float)(a.Y*v),(float)(a.Z*v));
      }

    #region Operator Overloads

    public static bool operator==(Vector3 a, Vector3 b) {
      if( ((Object)a) == null && ((Object)b) == null) return true;
      else if( ((Object)a) == null || ((Object)b) == null) return false;
      return a.Equals(b);
      }

    public static bool operator!=(Vector3 a, Vector3 b) {
      return !(a == b);
      }

    #endregion

    public override bool Equals(object obj) {
      Vector3 other = (Vector3)obj;
      
      if(other == null) return false;
      if(ReferenceEquals(this,obj)) return true;

      // If values are within 1 one millionth of each other consider them the same.
      if(Math.Abs(X-other.X) < .000001d &&
         Math.Abs(Y-other.Y) < .000001d &&
         Math.Abs(Z-other.Z) < .000001d)
        return true;

      return false;
      }

    public override int GetHashCode() {
      return ToString().GetHashCode();
      }

    public override string ToString() {
      return "(" + X + "," + Y + "," + Z + ")";
      }
    }

  }
